﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework;
using System.Xml;

namespace BrickBreaker.Core
{
    public class BBBrickManager
    {
        #region DATA_MEMBERS
        //
        private string testCollide = "Brick - No Collision";
        private BBBrick hitBrick;
        
        private List<BBBrick> bricks;

        //private Vector2 startLoc;

        private const int MAX_COLUMNS = 12;
        private const int MAX_ROWS = 10;

        private int index;
        private int count;
        //private int layoutChoice;

        private bool doneAddingMultiplierBonus = false;
        private int multiplier = 1;
        //
        #endregion DATA_MEMBERS



        #region PROPERTIES
        //
        public int BricksLeft
        {
            get
            {
                int left = bricks.Count;
                // Calculates the True number of Breakable bricks only
                foreach (BBBrick brick in bricks)
                {
                    if (brick.Density == BrickDensity.SOLID)
                    {
                        left--;
                    }
                }
                return left;
            }//get
        }

        /// <summary>
        /// Returns whether or not all bricks in the level were destroyed.
        /// </summary>
        public bool AllDestroyed
        {
            get { return (BricksLeft == 0); }
        }

        //-------------------------------------------------------------------------------------------
        //M0--: Get, Should call and register ComplexCollision ONCE, until reflection has occured
        //this State is internally updated in: Intersects(rect), hasCollided(sprite), ComplexCollision(sprite)
        public bool HasRegisteredCollision
        {
            get { return this.hitBrick.HasRegisteredCollision; }
            //set { this.hasRegisteredCollision = value; }
        }

        //public bool MultiplierActive
        //{
        //    get { return doneAddingMultiplierBonus; }
        //    set { doneAddingMultiplierBonus = value; }
        //}

        public int Multiplier
        {
            get { return multiplier; }
            set
            {
                multiplier = value;

                if (multiplier == 1) { doneAddingMultiplierBonus = true; }
                else { doneAddingMultiplierBonus = false; }
            }
        }
        //
        #endregion PROPERTIES



        #region PRIVATE METHODS

        /// <summary>
        /// Sets the position of all bricks.
        /// </summary>
        /// <param name="rows">How many rows of bricks.</param>
        /// <param name="columns">How many columns of bricks.</param>
        ////private void setPositions(int rows, int columns)
        ////{
        ////    //for (int i = 0; i < rows; i++)
        ////    //{
        ////    //    for (int j = 0; j < columns; j++)
        ////    //    {
        ////    //        /* set the position of each brick based off the start position*/
        ////    //        bricks[i, j].Position = new Vector2(startLoc.X + (bricks[i, j].Size.X * j), startLoc.Y + (i * bricks[i, j].Size.Y));
        ////    //        bricks[i, j].calculateBrickVertices();
        ////    //        bricksLeft++;
        ////    //    }
        ////    //}
        ////    int r = 0;
        ////    int c = 0;

        ////    int numOfBricks = (rows * columns);

        ////    for (int i = 0; i < numOfBricks; i++)
        ////    {
        ////        if(r >= rows)
        ////        { r = 0; }

        ////        if(c >= columns)
        ////        { c = 0; r++;}

        ////        bricks[i].Position = new Vector2(startLoc.X + (bricks[i].Size.X * c), startLoc.Y + (bricks[i].Size.Y * r));
        ////        //bricks[i].calculateBrickVertices();

        ////        c++;
        ////    }
        ////}

        #endregion PRIVATE METHODS



        #region PUBLIC_METHODS
        /// <summary>
        /// Creates a manager for all bricks to be handled by.
        /// </summary>
        /// <param name="startPos">The starting position of first brick.</param>
        /// <param name="rows">How many rows of bricks.</param>
        /// <param name="cols">How many columns of bricks.</param>
        //public BBBrickManager(Vector2 startPos, int rows, int cols)
        public BBBrickManager() //(Vector2 startPos, int rows, int cols)
        {
            //set the start position for use internally
            //this.startLoc = startPos;         //capture the starting position of thr bricks' layout grid
            this.bricks = new List<BBBrick>();  //instantiate 'bricks' list
            this.hitBrick = null;               //By default, no bricks destroyed
            
            #region Bounds Check
            ////if (rows > MAX_ROWS)
            ////{
            ////    rows = MAX_ROWS;
            ////}

            ////if(cols > MAX_COLUMNS)
            ////{
            ////    cols = MAX_COLUMNS;
            ////}
            #endregion

            #region HARD_CODED_BRICK_LAYOUT
            //layoutChoice = 1;

            #region First way
            ////if (layoutChoice == 1)
            ////{
            ////    //index = 0;
            ////    index = 1;  // ignore red for now until the texture gets replaced
            ////    int counter = 0;

            ////    for (int i = 0; i < (rows * cols); i++)
            ////    {
            ////        if (index > 3)
            ////        {
            ////            index = 1;
            ////        }
            ////        BBBrick brick = new BBBrick((BrickType)index, counter++);
            ////        bricks.Add(brick);
            ////        index++;
            ////    }
            ////}
            #endregion

            #region Second Way
            ////else if (layoutChoice == 2)
            ////{
            ////    count = 0;
            ////    //index = 0;
            ////    index = 1;
            ////    int counter = 0;

            ////    for (int i = 0; i < (rows * cols); i++)
            ////    {
            ////        if (count > cols - 1)
            ////        {
            ////            count = 0;
            ////            index++;

            ////            if (index > 4)
            ////            {
            ////                index = 1;
            ////            }
            ////        }

            ////        BBBrick brick = new BBBrick((BrickType)index, counter++);
            ////        bricks.Add(brick);
            ////        count++;
            ////    }
            ////}
            #endregion

            #region Third way
            ////else
            ////{
            ////    Random rand = new Random();
            ////    //index = 0;
            ////    index = 1;
            ////    int counter = 0;

            ////    for (int i = 0; i < (rows * cols); i++)
            ////    {
            ////        index = rand.Next(1, 5);

            ////        BBBrick brick = new BBBrick((BrickType)index, counter++);
            ////        bricks.Add(brick);
            ////    }
            ////}
            #endregion
            #endregion HARD_CODED_BRICK_LAYOUT

            //set each bricks position
            //setPositions(rows, cols);
        }

        //Use this method to generate bricks from any level
        public void GenerateBricks(List<BBBrick> bricks)
        {
            BBBrick brick;
            foreach (BBBrick b in bricks)
            {
                brick = new BBBrick(b.Type, b.HitCount, b.Density, b.ScoreValue);
                brick.Position = new Vector2(b.Position.X, b.Position.Y);
                this.bricks.Add(brick);
            }
        }

        //Convert UML to BBBrick
        public static List<BBBrick> ConvertUML2Bricks(string filename)
        {
            #region LOCAL_VARs
            BrickType type = BrickType.BLUE;
            BrickDensity density = BrickDensity.REGULAR;
            int hitCount = 0, scoreValue = 0;
            Vector2 position = Vector2.Zero, size = Vector2.Zero, center = Vector2.Zero;
            Vector2 speed = Vector2.Zero, direction = Vector2.Zero;
            #endregion

            List<BBBrick> bricks = new List<BBBrick>();
            BBBrick temp;

            #region READ_XML
            XmlTextReader reader = new XmlTextReader(filename); //("test.xml");

            while (reader.Read())
            {
                if (reader.IsStartElement() && reader.Name == "BRICK")
                {
                    #region ONE_NODE_AT_A_TIME
                    while (reader.Read())
                    {
                        switch (reader.Name)
                        {
                            case "TYPE":
                                type = (BrickType)Enum.Parse(typeof(BrickType), reader.ReadString().ToUpper());
                                break;
                            case "DENSITY":
                                density = (BrickDensity)Enum.Parse(typeof(BrickDensity), reader.ReadString().ToUpper());
                                break;
                            case "HITCOUNT":
                                hitCount = int.Parse(reader.ReadString());
                                break;
                            case "SCOREVALUE":
                                scoreValue = int.Parse(reader.ReadString());
                                break;
                            case "POSITION":
                                string[] arr1 = reader.ReadString().Split();
                                position = new Vector2(int.Parse(arr1[0]), int.Parse(arr1[1]));
                                break;
                            case "SIZE":
                                string[] arr2 = reader.ReadString().Split();
                                size = new Vector2(int.Parse(arr2[0]), int.Parse(arr2[1]));
                                break;
                            case "CENTER":
                                string[] arr3 = reader.ReadString().Split();
                                center = new Vector2(int.Parse(arr3[0]), int.Parse(arr3[1]));
                                break;
                            case "SPEED":
                                string[] arr4 = reader.ReadString().Split();
                                speed = new Vector2(int.Parse(arr4[0]), int.Parse(arr4[1]));
                                break;
                            case "DIRECTION":
                                string[] arr5 = reader.ReadString().Split();
                                direction = new Vector2(int.Parse(arr5[0]), int.Parse(arr5[1]));
                                break;
                        }//switch
                        //One Brick Node complete
                        if (reader.Name == "BRICK" && reader.NodeType == XmlNodeType.EndElement)
                        {
                            temp = new BBBrick(type, hitCount, density, scoreValue);
                            temp.Position = new Vector2(position.X, position.Y);
                            bricks.Add(temp);

                            temp = new BBBrick(BrickType.BLUE, 0, BrickDensity.REGULAR, 0);
                        }
                    }//inner-while
                    #endregion
                }//if
            }//outer-while
            reader.Close();
            #endregion

            return bricks;
        }

        /// <summary>
        /// Updates the manager and all bricks handled by it.
        /// </summary>
        public void Update()
        {
            foreach (BBBrick brick in bricks)
            {
                brick.Update();
                
                if (!doneAddingMultiplierBonus)
                {
                    brick.MultiplyScoreValue(multiplier);
                }
            }
            doneAddingMultiplierBonus = true;
            // set the multiplier back to 1
            // internally sets doneAddingMultiplierBonus = true
            //Multiplier = 1;
        }

        /// <summary>
        /// Checks whether any bricks have collided with an object.
        /// </summary>
        /// <param name="sprite">The object being checked for collision.</param>
        public bool hasCollided(BBSingleSprite sprite)
        {
            bool collided = false;

            foreach (BBBrick brick in bricks)
            {
                if (brick.hasCollided(sprite))
                {
                    // assign the brick that was hit
                    hitBrick = brick;

                    if (brick.HitCount == 0 && brick.Density != BrickDensity.SOLID)
                    {
                        // add score value to hud here.

                        // remove / destroy the brick
                        bricks.Remove(brick);
                        
                    }
                    collided = true;
                    
                    break;
                }
            }

            // if no collision
            if (!collided) { hitBrick = null; }
              
            return collided;
        }

        /// <summary>
        /// Checks whether any bricks have collided with an object.
        /// </summary>
        /// <param name="sprite">The object being checked for collision.</param>
        public bool hasCollided(BBProjectile projectile)
        {
            bool collided = false;

            foreach (BBBrick brick in bricks)
            {
                if (brick.hasCollided(projectile))
                {
                    // assign the brick that was hit
                    hitBrick = brick;

                    if (brick.HitCount == 0 && brick.Density != BrickDensity.SOLID)
                    {
                        // add score value to hud here.

                        // remove / destroy the brick
                        bricks.Remove(brick);

                    }
                    collided = true;

                    break;
                }
            }

            // if no collision
            if (!collided) { hitBrick = null; }

            return collided;
        }

        /// <summary>
        /// Draws all the bricks being handled by the manager.  Call this in a spritebatch.Begin()/End() block.
        /// </summary>
        /// <param name="spriteBatch">Your spritebatch.  Make sure you have already called begin().</param>
        public void Draw(SpriteBatch spriteBatch)
        {
            // draw all bricks first
            foreach (BBBrick brick in bricks)
            {
                brick.Draw(spriteBatch);

                if (BBGlobalConfig.DisplayDebug == true)
                {
                    spriteBatch.DrawString(BBGlobalConfig.font, testCollide, new Vector2(50, 500), Color.Red);
                }
                //spriteBatch.DrawString(BBGlobalConfig.font, brick.Position.X.ToString() + " " + brick.Position.Y.ToString(), brick.Position, Color.Red);
            }
        }

        /// <summary>
        /// Called when AABB collision checked passes. Calculates the reflection direction
        /// the ball shall travel once reflected off brick.
        /// </summary>
        /// <param name="sprite">The object being checked for collision.</param>
        public Vector2 ComplexCollision(BBSingleSprite sprite)
        {
            //return hitBrick.ComplexCollision(sprite);
            return hitBrick.ComplexCollision2(sprite);
        }

        /// <summary>
        /// Retrieves the position of the hit brick
        /// </summary>
        public Vector2 GetBrickPosition()
        {
            if (hitBrick == null)
                return Vector2.Zero;

            return hitBrick.Position + (hitBrick.Size / 2);
        }

        /// <summary>
        /// Retrieves the specific brick that was hit
        /// </summary>
        public BBBrick GetHitBrick()
        {
            if (hitBrick == null)
                return null;

            return hitBrick;
        }

        public void ResetBrickScore(int diviser)
        {
            foreach (BBBrick brick in bricks)
            {
                brick.DivideScoreValue(diviser);
            }
        }
        //
        #endregion PUBLIC_METHODS

    }//end class
}// end namespace
